<?php
/*
 * Author: Ryan Gilfether
 * URL: http://www.gilfether.com/phpCrypt
 * Date: April 3, 2013
 * Copyright (C) 2013 Ryan Gilfether
 *
 * This file is part of phpCrypt
 *
 * phpCrypt is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
 */

namespace PHP_CRYPT;
require_once(dirname(__FILE__)."/../Cipher.php");


/**
 * Base class to implement Rijndael (and AES)
 * This class can not be used directly, instead one
 * one of the child classes that inherit this class
 * should be used.
 * References used to implement this cipher:
 * http://www.net-security.org/dl/articles/AESbyExample.pdf
 * http://www.quadibloc.com/crypto/co040401.htm
 * http://www.inconteam.com/software-development/41-encryption/55-aes-test-vectors
 * FIPS 197 (fips-197.pdf)
 *
 * @author Ryan Gilfether
 * @link http://www.gilfether.com/phpcrypt
 * @copyright 2013 Ryan Gilfether
 */
abstract class Cipher_Rijndael extends Cipher
{
    /** @type string $xkey The expanded key */
    private $xkey = "";

    /**
     * @type array $_key_sizes The accepted key sizes in bytes,
     * this should be considered a constant
     */
    private static $_key_sizes = array(16, 24, 32);


    // THE FOLLOWING TABLES ARE INITIALIZED IN initTables()

    /**
     * @type array $_s The sbox,
     * this should be considered a constant
     */
    private static $_s = array();

    /**
     * @type array $_s_inv The inverse sbox
     * this should be considered a constant
     */
    private static $_s_inv = array();

    /**
     * @type array $_rcon The round constant,
     * this should be considered a constant
     */
    private static $_rcon = array();

    /**
     * @type array $s_matrix_mult The matrix multiplication table,
     * this should be considered a constant
     */
    private static $_matrix_mult = array();

    /**
     * @type array $_matrix_mult_inv The matrix multiplication
     * inverse table, this should be considered a constant
     */
    private static $_matrix_mult_inv = array();

    /*
	 * Galois Multiplication lookup tables,
	 * initialized in initTables()
	 */
    /**
     * @type array $_gm2 The Galois Multiplication table
     * for muliplying by 2, this should be considered a constant
     */
    private static $_gm2 = array();

    /**
     * @type array $_gm3 The Galois Multiplication table
     * for muliplying by 3, this should be considered a constant
     */
    private static $_gm3 = array();

    /**
     * @type array $_gm9 The Galois Multiplication table
     * for muliplying by 9, this should be considered a constant
     */
    private static $_gm9 = array();

    /**
     * @type array $_gm11 The Galois Multiplication table
     * for muliplying by 11, this should be considered a constant
     */
    private static $_gm11 = array();

    /**
     * @type array $_gm13 The Galois Multiplication table
     * for muliplying by 13, this should be considered a constant
     */
    private static $_gm13 = array();

    /**
     * @type array $_gm14 The Galois Multiplication table
     * for muliplying by 14, this should be considered a constant
     */
    private static $_gm14 = array();


    /**
     * Constructor
     * Sets the key used for encryption. Also sets the requied block size
     *
     * @param string The cipher name as set in a constant in the child class
     * @param string $key string containing the user supplied encryption key
     * @param integer $len Optional, the key size in bytes - used only by the AES child classes
     * @return void
     */
    public function __construct($cipher_name, $key, $len = 0)
    {
        // AES will pass in a $len, since it has a fixed key size, other
        // rijndael implementations can use variable key sizes, supported
        // sizes are stored in self::$_key_sizes
        if ($len == 0)
        {
            // the key must be one of the following lengths: 16, 24, 32 bytes
            $len = strlen($key);
            if (!in_array($len, self::$_key_sizes))
            {
                $msg  = "Incorrect key length for ".strtoupper($cipher_name).". ";
                $msg .= "Received $len bytes.";
                trigger_error($msg, E_USER_WARNING);
            }
        }

        // Setup the key
        parent::__construct($cipher_name, $key, $len);

        // initialize the tables used for rijndael/aes
        $this->initTables();
    }


    /**
     * Destructor
     *
     * @return void
     */
    public function __destruct()
    {
        parent::__destruct();
    }


    /**
     * Performs Rijndael encryption
     *
     * @param string @text The string to encrypt
     * @return boolean Returns true
     */
    public function encrypt(&$text)
    {
        // set the operation to decryption
        $this->operation(parent::ENCRYPT);

        $loops = 0;
        $key_sz = $this->keySize();
        $blk_sz = $this->blockSize();

        // if the key and block size is 16, do 10 rounds
        // if the key or block size is 24, and neither is longer than 24, do 12 rounds
        // if either key or block size is 32, do 14 rounds
        if ($key_sz == 16 && $blk_sz == 16) {
                    $loops = 10;
        } else if (($key_sz == 24 || $blk_sz == 24) && $key_sz <= 24 && $blk_sz <= 24) {
                    $loops = 12;
        } else if ($key_sz == 32 || $blk_sz == 32) {
                    $loops = 14;
        }

        // now begin the encryption
        $this->addRoundKey($text, 0);

        for ($i = 1; $i <= $loops; ++$i)
        {
            $this->byteSub($text);
            $this->shiftRow($text);

            // the last iteration does not use mixColumn
            if ($i < $loops) {
                            $this->mixColumn($text);
            }

            $this->addRoundKey($text, $i);
        }

        return true;
    }


    /**
     * Performs Rijndael decryption
     *
     * @param string @text The string to decrypt
     * @return boolean Returns true
     */
    public function decrypt(&$text)
    {
        // set the operation to decryption
        $this->operation(parent::DECRYPT);

        $loops = 0;
        $key_sz = $this->keySize();
        $blk_sz = $this->blockSize();

        // if the key and block size is 16, do 10 rounds
        // if the key or block size is 24, and neither is longer than 24, do 12 rounds
        // if either key or block size is 32, do 14 rounds
        if ($key_sz == 16 && $blk_sz == 16) {
                    $loops = 10;
        } else if (($key_sz == 24 || $blk_sz == 24) && $key_sz <= 24 && $blk_sz <= 24) {
                    $loops = 12;
        } else if ($key_sz == 32 || $blk_sz == 32) {
                    $loops = 14;
        }

        // now begin the decryption
        $this->addRoundKey($text, 0);

        for ($i = 1; $i <= $loops; ++$i)
        {
            $this->shiftRow($text);
            $this->byteSub($text);
            $this->addRoundKey($text, $i);

            // the last iteration does not use mixColumn
            if ($i < $loops) {
                            $this->mixColumn($text);
            }
        }

        return true;
    }


    /**
     * Indicates that this is a block cipher
     *
     * @return integer Returns Cipher::BLOCK
     */
    public function type()
    {
        return parent::BLOCK;
    }


    /**
     * Do the multiplication required in mixColumn()
     * We follow the description the multiplication from Wikipedia:
     * http://en.wikipedia.org/wiki/Advanced_Encryption_Standard#The_MixColumns_step
     *
     * @param integer $m A value from self::$_matrix_mult or self::$_matrix_mult_inv
     * @param integer $byte The value to multipy by $m
     * @return integer The result of the multiplication
     */
    protected function mixColumnMultiply($m, $byte)
    {
        // if multiplying by 1, then we just return the same number
        if ($m == 0x01) {
                    return $byte;
        }

        $hex = parent::dec2Hex($byte);
        $row = parent::hex2Dec($hex[0]);
        $col = parent::hex2Dec($hex[1]);
        $pos = ($row * 16) + $col;

        // multiply by 2 (comes from self::$_matrix_mult during encryption)
        if ($m == 0x02) {
                    return self::$_gm2[$pos];
        }

        // multiply by 3 (comes from self::$_matrix_mult during encryption)
        if ($m == 0x03) {
                    return self::$_gm3[$pos];
        }

        // multiply by 9 (comes from self::$_matrix_mult_inv during decryption)
        if ($m == 0x09) {
                    return self::$_gm9[$pos];
        }

        // multiply by 11 (comes from self::$_matrix_mult_inv during decryption)
        if ($m == 0x0b) {
                    return self::$_gm11[$pos];
        }

        // multiply by 13 (comes from self::$_matrix_mult_inv during decryption)
        if ($m == 0x0d) {
                    return self::$_gm13[$pos];
        }

        // multiply by 14 (comes from self::$_matrix_mult_inv during decryption)
        if ($m == 0x0e) {
                    return self::$_gm14[$pos];
        }
    }


    /**
     * Each time this function is called, it XORs the 16 bytes of $text
     * with the next 16 bytes from the $this->xkey. The expanded key
     * never has the same bytes used twice. Note that the first time
     * addRoundKey() is called, it will be outside of the rounds, so no
     * $round is given. Every call after that will be inside the rounds.
     *
     * @param string $text The text to encrypt/decrypt
     * @param integer $round The round we are on inside of rijndael()
     * @return void
     */
    private function addRoundKey(&$text, $round)
    {
        // length of the xkey
        $ek_len = strlen($this->xkey);
        $len = $this->blockSize();

        if ($this->operation() == parent::ENCRYPT) {
                    $offset = $round * $len;
        } else {
                    $offset = ($ek_len - ($round * $len)) - $len;
        }

        for ($i = 0; $i < $len; ++$i) {
                    $text[$i] = $text[$i] ^ $this->xkey[$offset + $i];
        }
    }


    /**
     * Applies the Sbox to each byte of the string passed in
     * This is used in key expansione
     *
     * @param string $text The string to peform the byte subsitution
     * @return void
     */
    private function byteSub(&$text)
    {
        $max = strlen($text);
        for ($i = 0; $i < $max; ++$i)
        {
            // the sbox is arrange in a 16 x 16 grid, where each row
            // and column is numbered in hex (from 0 - f)
            $hex = parent::str2Hex($text[$i]);
            $row = parent::hex2Dec($hex[0]);
            $col = parent::hex2Dec($hex[1]);
            $pos = ($row * 16) + $col;

            // return the corresponding value from the sbox
            if ($this->operation() == parent::ENCRYPT) {
                            $text[$i] = chr(self::$_s[$pos]);
            } else {
                // parent::DECRYPT uses the inverse sbox
                $text[$i] = chr(self::$_s_inv[$pos]);
            }
        }
    }


    /**
     * This function is hard to explain, the easiest way to understand it is to read
     * http://www.net-security.org/dl/articles/AESbyExample.pdf, Section 5.4
     *
     * @param string $t The string to multiply bytes by the Galois Multiplication lookup tables
     * @return void
     */
    private function mixColumn(&$t)
    {
        $tmp = $t;

        // the matrix we use depends on if we are encrypting or decrypting
        if ($this->operation() == parent::ENCRYPT) {
                    $m = self::$_matrix_mult;
        } else {
            // parent::DECRYPT
            $m = self::$_matrix_mult_inv;
        }

        // the number of rounds we make depends on the block size of the text
        // used during encryption/decryption
        // 128 has a 4x4 matrix, loop 4 times
        // 192 has a 4x6 matrix, loop 6 times
        // 256 has a 4x8 matrix, loop 8 times
        $max_col = ($this->blockSize() * 8) / 32;

        // loop through each column of the matrix
        for ($col = 0; $col < $max_col; ++$col)
        {
            $pos = $col * 4;

            $a = $this->mixColumnMultiply($m[0], ord($tmp[$pos + 0]));
            $b = $this->mixColumnMultiply($m[4], ord($tmp[$pos + 1]));
            $c = $this->mixColumnMultiply($m[8], ord($tmp[$pos + 2]));
            $d = $this->mixColumnMultiply($m[12], ord($tmp[$pos + 3]));
            $t[$pos + 0] = chr($a ^ $b ^ $c ^ $d);

            $a = $this->mixColumnMultiply($m[1], ord($tmp[$pos + 0]));
            $b = $this->mixColumnMultiply($m[5], ord($tmp[$pos + 1]));
            $c = $this->mixColumnMultiply($m[9], ord($tmp[$pos + 2]));
            $d = $this->mixColumnMultiply($m[13], ord($tmp[$pos + 3]));
            $t[$pos + 1] = chr($a ^ $b ^ $c ^ $d);

            $a = $this->mixColumnMultiply($m[2], ord($tmp[$pos + 0]));
            $b = $this->mixColumnMultiply($m[6], ord($tmp[$pos + 1]));
            $c = $this->mixColumnMultiply($m[10], ord($tmp[$pos + 2]));
            $d = $this->mixColumnMultiply($m[14], ord($tmp[$pos + 3]));
            $t[$pos + 2] = chr($a ^ $b ^ $c ^ $d);

            $a = $this->mixColumnMultiply($m[3], ord($tmp[$pos + 0]));
            $b = $this->mixColumnMultiply($m[7], ord($tmp[$pos + 1]));
            $c = $this->mixColumnMultiply($m[11], ord($tmp[$pos + 2]));
            $d = $this->mixColumnMultiply($m[15], ord($tmp[$pos + 3]));
            $t[$pos + 3] = chr($a ^ $b ^ $c ^ $d);
        }
    }


    /**
     * Convert the 16, 24, or 32 bytes of $text into a matrix, and shift each row
     * n-bytes left for encryptiong, n-bytes right if we are decrypting.
     * Row shifts depend on the bit size of the block $text.
     * Rijndael-128/AES: 4x4 matrix
     * Rijndael-192:	6x4 matrix
     * Rijndael-256:	8x4 matrix
     *
     * @param string $text A 16, 24, or 32 byte string
     * @return void
     */
    private function shiftRow(&$text)
    {
        /*
		 * Rijndael-128 / AES
		 */
        if ($this->blockSize() == 16)
        {
            if ($this->operation() == parent::ENCRYPT)
            {
                // create a 4x4 matrix
                // row 0 is unchanged,
                // shift row 1 left 1 byte
                // shift row 2 left 2 bytes
                // shift row 3 left 3 bytes
                $text = $text[0].$text[5].$text[10].$text[15].$text[4].$text[9].
                        $text[14].$text[3].$text[8].$text[13].$text[2].$text[7].
                        $text[12].$text[1].$text[6].$text[11];
            } else // parent::DECRYPT
            {
                // create a 4x4 matrix
                // row 0 is unchanged,
                // shift row 1 right 1 byte
                // shift row 2 right 2 bytes
                // shift row 3 right 3 bytes
                $text = $text[0].$text[13].$text[10].$text[7].$text[4].$text[1].
                        $text[14].$text[11].$text[8].$text[5].$text[2].$text[15].
                        $text[12].$text[9].$text[6].$text[3];
            }
        }

        /*
		 * Rijndael-192
		 */
        if ($this->blockSize() == 24)
        {
            if ($this->operation() == parent::ENCRYPT)
            {
                // create a 6x4 matrix
                // row 0 is unchanged
                // shift row 1 left 1 byte
                // shift row 2 left 2 bytes
                // shift row 3 left 3 bytes
                $text = $text[0].$text[5].$text[10].$text[15].$text[4].$text[9].
                        $text[14].$text[19].$text[8].$text[13].$text[18].$text[23].
                        $text[12].$text[17].$text[22].$text[3].$text[16].$text[21].
                        $text[2].$text[7].$text[20].$text[1].$text[6].$text[11];

            } else // parent::DECRYPT
            {
                // create a 6x4 matrix
                // row 0 is unchanged
                // shift row 1 right 1 byte
                // shift row 2 right 2 bytes
                // shift row 3 right 3 bytes
                $text = $text[0].$text[21].$text[18].$text[15].$text[4].$text[1].
                        $text[22].$text[19].$text[8].$text[5].$text[2].$text[23].
                        $text[12].$text[9].$text[6].$text[3].$text[16].$text[13].
                        $text[10].$text[7].$text[20].$text[17].$text[14].$text[11];
            }
        }

        /*
		 * Rijndael-256
		 */
        if ($this->blockSize() == 32)
        {
            if ($this->operation() == parent::ENCRYPT)
            {
                // create an 8x4 matrix
                // row 0 is unchanged
                // shift row 1 left 1 byte
                // shift row 2 left 3 bytes
                // shift row 3 left 4 bytes
                $text = $text[0].$text[5].$text[14].$text[19].$text[4].$text[9].$text[18].
                        $text[23].$text[8].$text[13].$text[22].$text[27].$text[12].$text[17].
                        $text[26].$text[31].$text[16].$text[21].$text[30].$text[3].$text[20].
                        $text[25].$text[2].$text[7].$text[24].$text[29].$text[6].$text[11].
                        $text[28].$text[1].$text[10].$text[15];
            } else // parent::DECRYPT
            {
                // create an 8x4 matrix
                // row 0 is unchanged
                // shift row 1 right 1 byte
                // shift row 2 right 3 bytes
                // shift row 3 right 4 bytes
                $text = $text[0].$text[29].$text[22].$text[19].$text[4].$text[1].$text[26].
                        $text[23].$text[8].$text[5].$text[30].$text[27].$text[12].$text[9].
                        $text[2].$text[31].$text[16].$text[13].$text[6].$text[3].$text[20].
                        $text[17].$text[10].$text[7].$text[24].$text[21].$text[14].$text[11].
                        $text[28].$text[25].$text[18].$text[15];
            }
        }
    }


    /**
     * Applies the Sbox to each byte of the string passed in.
     * This is similar to subByte(), but Unlike subByte() we do not use
     * the _s_inv[] table. This function is only used in expandKey(),
     * which is implemented by the class that inherits this class
     *
     * @param string $text The string to peform the byte subsitution
     * @return string The string with the subsituted bytes
     */
    private function subWord(&$text)
    {
        $max = strlen($text);
        for ($i = 0; $i < $max; ++$i)
        {
            // the sbox is arrange in a 16 x 16 grid, where each row
            // and column is numbered in hex (from 0 - f)
            $hex = parent::str2Hex($text[$i]);
            $row = parent::hex2Dec($hex[0]);
            $col = parent::hex2Dec($hex[1]);
            $pos = ($row * 16) + $col;

            $text[$i] = chr(self::$_s[$pos]);
        }
    }


    /**
     * Rotate a 4 byte block of the key, moving the first byte to
     * to the end, and shifting everything left
     * Used in key Expandsion
     *
     * @param string $key_block A 4 byte string
     * @return string The shifted 4 byte string
     */
    private function rotWord($key_block)
    {
        return substr($key_block, 1, 3).$key_block[0];
    }


    /**
     * Returns 4 bytes from the expanded key starting at the given offset
     * Used during expandKey()
     *
     * @param integer $offset The offset within $this->xkey to grab the 4 bytes
     * @return string A 4 byte string from the key
     */
    private function ek($offset)
    {
        return substr($this->xkey, $offset, 4);
    }


    /**
     * Returns 4 bytes of the original key from the given offset
     * Used during expandKey()
     *
     * @param integer $offset The offset within $this->key to grab the 4 bytes
     * @return string A 4 byte string from the key
     */
    private function k($offset)
    {
        return substr($this->key(), $offset, 4);
    }


    /**
     * Return the 4 byte round constant used during expandKey().
     * Gets the 1 byte value from self::$_rcon and multiplies it by
     * 0x01000000 to create a 4 byte value
     *
     * @param integer $pos The position in self::$_rcon array to grab 1 byte
     * @return integer A 4 byte value
     */
    private function rcon($pos)
    {
        return (self::$_rcon[$pos] * 0x01000000);
    }


    /**
     * Expands the key
     * The key expands based on the block size as well as the key size
     *
     * @return boolean|null
     */
    protected function expandKey()
    {
        if ($this->keySize() == 16) {
                    return $this->expandKey128();
        } else if ($this->keySize() == 24) {
                    return $this->expandKey192();
        } else if ($this->keySize() == 32) {
                    return $this->expandKey256();
        }
    }


    /**
     * Expand a 16 byte key, the size it is expanded to varies
     * based on the block size of the Rijndael implementation chosen
     *
     * @return boolean
     */
    private function expandKey128()
    {
        // clear the xkey, we're creating a new one
        $this->xkey = "";
        $max = 0;

        // the number of rounds we make depends on the block size of the text
        // used during encryption/decryption
        if ($this->blockSize() == 16) {
                    $max = 44;
        }
        if ($this->blockSize() == 24) {
                    $max = 78;
        }
        if ($this->blockSize() == 32) {
                    $max = 120;
        }

        // 16 byte key expands to 176 bytes
        for ($i = 0; $i < $max; ++$i)
        {
            if ($i >= 0 && $i <= 3) {
                            $this->xkey .= $this->k($i * 4);
            } else if (($i % 4) == 0)
            {
                // rotate the 4 bytes
                $subword = $this->rotWord($this->ek(($i - 1) * 4));

                // apply the sbox
                $this->subWord($subword);

                // return 4 byte value based on self::$_rcon table
                //$rcon = $this->rcon(($i / 4) - 1);
                $rcon = $this->rcon(($i / 4));

                // grab 4 bytes from $this->extended_key
                $ek = $this->ek(($i - 4) * 4);

                $h1 = parent::str2Hex($subword);
                $h2 = parent::dec2Hex($rcon);
                $h3 = parent::str2Hex($ek);
                $res = parent::xorHex($h1, $h2, $h3);
                $this->xkey .= parent::hex2Str($res);
            } else
            {
                $h1 = parent::str2Hex($this->ek(($i - 1) * 4));
                $h2 = parent::str2Hex($this->ek(($i - 4) * 4));
                $res = parent::xorHex($h1, $h2);
                $this->xkey .= parent::hex2Str($res);
            }
        }

        return true;
    }


    /**
     * Expand a 24 byte key, the size it is expanded to varies
     * based on the block size of the Rijndael implementation chosen
     *
     * @return boolean
     */
    private function expandKey192()
    {
        // clear the xkey, we're creating a new one
        $this->xkey = "";
        $max = 0;

        // the number of rounds we make depends on the block size of the text
        // used during encryption/decryption
        if ($this->blockSize() == 16) {
                    $max = 52;
        }
        if ($this->blockSize() == 24) {
                    $max = 78;
        }
        if ($this->blockSize() == 32) {
                    $max = 120;
        }

        // 24 byte key expands to 208 bytes
        for ($i = 0; $i < $max; ++$i)
        {
            if ($i >= 0 && $i <= 5) {
                            $this->xkey .= $this->k($i * 4);
            } else if (($i % 6) == 0)
            {
                // rotate the 4 bytes
                $subword = $this->rotWord($this->ek(($i - 1) * 4));

                // apply the sbox
                $this->subWord($subword);

                // return 4 byte value based on self::$_rcon table
                //$rcon = $this->rcon(($i / 6) - 1);
                $rcon = $this->rcon(($i / 6));

                // grab 4 bytes from $this->extended_key
                $ek = $this->ek(($i - 6) * 4);

                $h1 = parent::str2Hex($subword);
                $h2 = parent::dec2Hex($rcon);
                $h3 = parent::str2Hex($ek);
                $res = parent::xorHex($h1, $h2, $h3);
                $this->xkey .= parent::hex2Str($res);
            } else
            {
                $h1 = parent::str2Hex($this->ek(($i - 1) * 4));
                $h2 = parent::str2Hex($this->ek(($i - 6) * 4));
                $res = parent::xorHex($h1, $h2);
                $this->xkey .= parent::hex2Str($res);
            }
        }

        return true;
    }


    /**
     * Expand a 32 byte key, the size it is expanded to varies
     * based on the block size of the Rijndael implementation chosen
     *
     * @return boolean
     */
    private function expandKey256()
    {
        // clear the xkey, we're creating a new one
        $this->xkey = "";
        $max = 0;

        // the number of rounds we make depends on the block size of the text
        // used during encryption/decryption
        if ($this->blockSize() == 16) {
                    $max = 60;
        }
        if ($this->blockSize() == 24) {
                    $max = 90;
        }
        if ($this->blockSize() == 32) {
                    $max = 120;
        }

        // 32 byte key expands to 240 bytes
        for ($i = 0; $i < $max; ++$i)
        {
            if ($i >= 0 && $i <= 7) {
                            $this->xkey .= $this->k($i * 4);
            } else if ($i % 8 == 0)
            {
                // rotate the 4 bytes
                $subword = $this->rotWord($this->ek(($i - 1) * 4));

                // apply the sbox
                $this->subWord($subword);

                // return 4 byte value based on self::$_rcon table
                $rcon = $this->rcon(($i / 8));

                // grab 4 bytes from $this->extended_key
                $ek = $this->ek(($i - 8) * 4);

                $h1 = parent::str2Hex($subword);
                $h2 = parent::dec2Hex($rcon);
                $h3 = parent::str2Hex($ek);
                $res = parent::xorHex($h1, $h2, $h3);
                $this->xkey .= parent::hex2Str($res);
            } else if ($i % 4 == 0)
            {
                // get the subsitution from the s-box
                $subword = $this->ek(($i - 1) * 4);
                $this->subWord($subword);

                // get the extended key part
                $ek = $this->ek(($i - 8) * 4);

                // xor the two parts
                $h1 = parent::str2Hex($subword);
                $h2 = parent::str2Hex($ek);
                $res = parent::xorHex($h1, $h2);
                $this->xkey .= parent::hex2Str($res);
            } else
            {
                $h1 = parent::str2Hex($this->ek(($i - 1) * 4));
                $h2 = parent::str2Hex($this->ek(($i - 8) * 4));
                $res = parent::xorHex($h1, $h2);
                $this->xkey .= parent::hex2Str($res);
            }
        }

        return true;
    }


    /**
     * Initalizes the tables used for Rijndael/AES encryption
     *
     * @return void
     */
    private function initTables()
    {
        // the sbox used for encryption
        self::$_s = array(
            0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
            0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
            0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
            0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
            0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
            0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
            0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
            0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
            0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
            0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
            0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
            0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
            0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
            0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
            0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
            0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
        );

        // the inverse sbox used for decryption
        self::$_s_inv = array(
            0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
            0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
            0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
            0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
            0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
            0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
            0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
            0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
            0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
            0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
            0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
            0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
            0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
            0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
            0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
            0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
        );

        // used in mixColumn() during encryption
        self::$_matrix_mult = array(
            0x02, 0x01, 0x01, 0x03,
            0x03, 0x02, 0x01, 0x01,
            0x01, 0x03, 0x02, 0x01,
            0x01, 0x01, 0x03, 0x02
        );

        // used in mixColumn() during decryption
        self::$_matrix_mult_inv = array(
            0x0e, 0x09, 0x0d, 0x0b,
            0x0b, 0x0e, 0x09, 0x0d,
            0x0d, 0x0b, 0x0e, 0x09,
            0x09, 0x0d, 0x0b, 0x0e
        );

        // The round constants, each round is a 1 byte value which should be multiplied by 0x01000000
        // to create a 4 byte value before being used in expandKey(). This is done in rcon()
        // NOTE: AES only needs the first row of values, since AES only uses 16 byte blocks,
        // the other values are used for larger block/key combinations supported by Rijndael
        // NOTE: self::$_rcon[0] is never used
        self::$_rcon = array(
            0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
            0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39,
            0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a,
            0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8,
            0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef,
            0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc,
            0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b,
            0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3,
            0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94,
            0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20,
            0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35,
            0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f,
            0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04,
            0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63,
            0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd,
            0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d
        );

        /*
		 * Galois Multiplication lookup tables
		 * See http://en.wikipedia.org/wiki/Rijndael_mix_columns#InverseMixColumns
		 */

        // multiply a byte by 2 (the value 2 will come from self::$_matrix_mult)
        self::$_gm2 = array(
            0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
            0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e,
            0x40, 0x42, 0x44, 0x46, 0x48, 0x4a, 0x4c, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e,
            0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7e,
            0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9c, 0x9e,
            0xa0, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xac, 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, 0xbe,
            0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce, 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde,
            0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec, 0xee, 0xf0, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfc, 0xfe,
            0x1b, 0x19, 0x1f, 0x1d, 0x13, 0x11, 0x17, 0x15, 0x0b, 0x09, 0x0f, 0x0d, 0x03, 0x01, 0x07, 0x05,
            0x3b, 0x39, 0x3f, 0x3d, 0x33, 0x31, 0x37, 0x35, 0x2b, 0x29, 0x2f, 0x2d, 0x23, 0x21, 0x27, 0x25,
            0x5b, 0x59, 0x5f, 0x5d, 0x53, 0x51, 0x57, 0x55, 0x4b, 0x49, 0x4f, 0x4d, 0x43, 0x41, 0x47, 0x45,
            0x7b, 0x79, 0x7f, 0x7d, 0x73, 0x71, 0x77, 0x75, 0x6b, 0x69, 0x6f, 0x6d, 0x63, 0x61, 0x67, 0x65,
            0x9b, 0x99, 0x9f, 0x9d, 0x93, 0x91, 0x97, 0x95, 0x8b, 0x89, 0x8f, 0x8d, 0x83, 0x81, 0x87, 0x85,
            0xbb, 0xb9, 0xbf, 0xbd, 0xb3, 0xb1, 0xb7, 0xb5, 0xab, 0xa9, 0xaf, 0xad, 0xa3, 0xa1, 0xa7, 0xa5,
            0xdb, 0xd9, 0xdf, 0xdd, 0xd3, 0xd1, 0xd7, 0xd5, 0xcb, 0xc9, 0xcf, 0xcd, 0xc3, 0xc1, 0xc7, 0xc5,
            0xfb, 0xf9, 0xff, 0xfd, 0xf3, 0xf1, 0xf7, 0xf5, 0xeb, 0xe9, 0xef, 0xed, 0xe3, 0xe1, 0xe7, 0xe5
        );

        // multiply a byte by 3 (the value 3 will come from self::$_matrix_mult)
        self::$_gm3 = array(
            0x00, 0x03, 0x06, 0x05, 0x0c, 0x0f, 0x0a, 0x09, 0x18, 0x1b, 0x1e, 0x1d, 0x14, 0x17, 0x12, 0x11,
            0x30, 0x33, 0x36, 0x35, 0x3c, 0x3f, 0x3a, 0x39, 0x28, 0x2b, 0x2e, 0x2d, 0x24, 0x27, 0x22, 0x21,
            0x60, 0x63, 0x66, 0x65, 0x6c, 0x6f, 0x6a, 0x69, 0x78, 0x7b, 0x7e, 0x7d, 0x74, 0x77, 0x72, 0x71,
            0x50, 0x53, 0x56, 0x55, 0x5c, 0x5f, 0x5a, 0x59, 0x48, 0x4b, 0x4e, 0x4d, 0x44, 0x47, 0x42, 0x41,
            0xc0, 0xc3, 0xc6, 0xc5, 0xcc, 0xcf, 0xca, 0xc9, 0xd8, 0xdb, 0xde, 0xdd, 0xd4, 0xd7, 0xd2, 0xd1,
            0xf0, 0xf3, 0xf6, 0xf5, 0xfc, 0xff, 0xfa, 0xf9, 0xe8, 0xeb, 0xee, 0xed, 0xe4, 0xe7, 0xe2, 0xe1,
            0xa0, 0xa3, 0xa6, 0xa5, 0xac, 0xaf, 0xaa, 0xa9, 0xb8, 0xbb, 0xbe, 0xbd, 0xb4, 0xb7, 0xb2, 0xb1,
            0x90, 0x93, 0x96, 0x95, 0x9c, 0x9f, 0x9a, 0x99, 0x88, 0x8b, 0x8e, 0x8d, 0x84, 0x87, 0x82, 0x81,
            0x9b, 0x98, 0x9d, 0x9e, 0x97, 0x94, 0x91, 0x92, 0x83, 0x80, 0x85, 0x86, 0x8f, 0x8c, 0x89, 0x8a,
            0xab, 0xa8, 0xad, 0xae, 0xa7, 0xa4, 0xa1, 0xa2, 0xb3, 0xb0, 0xb5, 0xb6, 0xbf, 0xbc, 0xb9, 0xba,
            0xfb, 0xf8, 0xfd, 0xfe, 0xf7, 0xf4, 0xf1, 0xf2, 0xe3, 0xe0, 0xe5, 0xe6, 0xef, 0xec, 0xe9, 0xea,
            0xcb, 0xc8, 0xcd, 0xce, 0xc7, 0xc4, 0xc1, 0xc2, 0xd3, 0xd0, 0xd5, 0xd6, 0xdf, 0xdc, 0xd9, 0xda,
            0x5b, 0x58, 0x5d, 0x5e, 0x57, 0x54, 0x51, 0x52, 0x43, 0x40, 0x45, 0x46, 0x4f, 0x4c, 0x49, 0x4a,
            0x6b, 0x68, 0x6d, 0x6e, 0x67, 0x64, 0x61, 0x62, 0x73, 0x70, 0x75, 0x76, 0x7f, 0x7c, 0x79, 0x7a,
            0x3b, 0x38, 0x3d, 0x3e, 0x37, 0x34, 0x31, 0x32, 0x23, 0x20, 0x25, 0x26, 0x2f, 0x2c, 0x29, 0x2a,
            0x0b, 0x08, 0x0d, 0x0e, 0x07, 0x04, 0x01, 0x02, 0x13, 0x10, 0x15, 0x16, 0x1f, 0x1c, 0x19, 0x1a
        );

        // multiply a byte by 9 (the value 9 will come from self::$_matrix_mult_inv)
        self::$_gm9 = array(
            0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77,
            0x90, 0x99, 0x82, 0x8b, 0xb4, 0xbd, 0xa6, 0xaf, 0xd8, 0xd1, 0xca, 0xc3, 0xfc, 0xf5, 0xee, 0xe7,
            0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04, 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c,
            0xab, 0xa2, 0xb9, 0xb0, 0x8f, 0x86, 0x9d, 0x94, 0xe3, 0xea, 0xf1, 0xf8, 0xc7, 0xce, 0xd5, 0xdc,
            0x76, 0x7f, 0x64, 0x6d, 0x52, 0x5b, 0x40, 0x49, 0x3e, 0x37, 0x2c, 0x25, 0x1a, 0x13, 0x08, 0x01,
            0xe6, 0xef, 0xf4, 0xfd, 0xc2, 0xcb, 0xd0, 0xd9, 0xae, 0xa7, 0xbc, 0xb5, 0x8a, 0x83, 0x98, 0x91,
            0x4d, 0x44, 0x5f, 0x56, 0x69, 0x60, 0x7b, 0x72, 0x05, 0x0c, 0x17, 0x1e, 0x21, 0x28, 0x33, 0x3a,
            0xdd, 0xd4, 0xcf, 0xc6, 0xf9, 0xf0, 0xeb, 0xe2, 0x95, 0x9c, 0x87, 0x8e, 0xb1, 0xb8, 0xa3, 0xaa,
            0xec, 0xe5, 0xfe, 0xf7, 0xc8, 0xc1, 0xda, 0xd3, 0xa4, 0xad, 0xb6, 0xbf, 0x80, 0x89, 0x92, 0x9b,
            0x7c, 0x75, 0x6e, 0x67, 0x58, 0x51, 0x4a, 0x43, 0x34, 0x3d, 0x26, 0x2f, 0x10, 0x19, 0x02, 0x0b,
            0xd7, 0xde, 0xc5, 0xcc, 0xf3, 0xfa, 0xe1, 0xe8, 0x9f, 0x96, 0x8d, 0x84, 0xbb, 0xb2, 0xa9, 0xa0,
            0x47, 0x4e, 0x55, 0x5c, 0x63, 0x6a, 0x71, 0x78, 0x0f, 0x06, 0x1d, 0x14, 0x2b, 0x22, 0x39, 0x30,
            0x9a, 0x93, 0x88, 0x81, 0xbe, 0xb7, 0xac, 0xa5, 0xd2, 0xdb, 0xc0, 0xc9, 0xf6, 0xff, 0xe4, 0xed,
            0x0a, 0x03, 0x18, 0x11, 0x2e, 0x27, 0x3c, 0x35, 0x42, 0x4b, 0x50, 0x59, 0x66, 0x6f, 0x74, 0x7d,
            0xa1, 0xa8, 0xb3, 0xba, 0x85, 0x8c, 0x97, 0x9e, 0xe9, 0xe0, 0xfb, 0xf2, 0xcd, 0xc4, 0xdf, 0xd6,
            0x31, 0x38, 0x23, 0x2a, 0x15, 0x1c, 0x07, 0x0e, 0x79, 0x70, 0x6b, 0x62, 0x5d, 0x54, 0x4f, 0x46
        );

        // multiply a byte by 11 (the value 11 will come from self::$_matrix_mult_inv)
        self::$_gm11 = array(
            0x00, 0x0b, 0x16, 0x1d, 0x2c, 0x27, 0x3a, 0x31, 0x58, 0x53, 0x4e, 0x45, 0x74, 0x7f, 0x62, 0x69,
            0xb0, 0xbb, 0xa6, 0xad, 0x9c, 0x97, 0x8a, 0x81, 0xe8, 0xe3, 0xfe, 0xf5, 0xc4, 0xcf, 0xd2, 0xd9,
            0x7b, 0x70, 0x6d, 0x66, 0x57, 0x5c, 0x41, 0x4a, 0x23, 0x28, 0x35, 0x3e, 0x0f, 0x04, 0x19, 0x12,
            0xcb, 0xc0, 0xdd, 0xd6, 0xe7, 0xec, 0xf1, 0xfa, 0x93, 0x98, 0x85, 0x8e, 0xbf, 0xb4, 0xa9, 0xa2,
            0xf6, 0xfd, 0xe0, 0xeb, 0xda, 0xd1, 0xcc, 0xc7, 0xae, 0xa5, 0xb8, 0xb3, 0x82, 0x89, 0x94, 0x9f,
            0x46, 0x4d, 0x50, 0x5b, 0x6a, 0x61, 0x7c, 0x77, 0x1e, 0x15, 0x08, 0x03, 0x32, 0x39, 0x24, 0x2f,
            0x8d, 0x86, 0x9b, 0x90, 0xa1, 0xaa, 0xb7, 0xbc, 0xd5, 0xde, 0xc3, 0xc8, 0xf9, 0xf2, 0xef, 0xe4,
            0x3d, 0x36, 0x2b, 0x20, 0x11, 0x1a, 0x07, 0x0c, 0x65, 0x6e, 0x73, 0x78, 0x49, 0x42, 0x5f, 0x54,
            0xf7, 0xfc, 0xe1, 0xea, 0xdb, 0xd0, 0xcd, 0xc6, 0xaf, 0xa4, 0xb9, 0xb2, 0x83, 0x88, 0x95, 0x9e,
            0x47, 0x4c, 0x51, 0x5a, 0x6b, 0x60, 0x7d, 0x76, 0x1f, 0x14, 0x09, 0x02, 0x33, 0x38, 0x25, 0x2e,
            0x8c, 0x87, 0x9a, 0x91, 0xa0, 0xab, 0xb6, 0xbd, 0xd4, 0xdf, 0xc2, 0xc9, 0xf8, 0xf3, 0xee, 0xe5,
            0x3c, 0x37, 0x2a, 0x21, 0x10, 0x1b, 0x06, 0x0d, 0x64, 0x6f, 0x72, 0x79, 0x48, 0x43, 0x5e, 0x55,
            0x01, 0x0a, 0x17, 0x1c, 0x2d, 0x26, 0x3b, 0x30, 0x59, 0x52, 0x4f, 0x44, 0x75, 0x7e, 0x63, 0x68,
            0xb1, 0xba, 0xa7, 0xac, 0x9d, 0x96, 0x8b, 0x80, 0xe9, 0xe2, 0xff, 0xf4, 0xc5, 0xce, 0xd3, 0xd8,
            0x7a, 0x71, 0x6c, 0x67, 0x56, 0x5d, 0x40, 0x4b, 0x22, 0x29, 0x34, 0x3f, 0x0e, 0x05, 0x18, 0x13,
            0xca, 0xc1, 0xdc, 0xd7, 0xe6, 0xed, 0xf0, 0xfb, 0x92, 0x99, 0x84, 0x8f, 0xbe, 0xb5, 0xa8, 0xa3
        );

        // multiply a byte by 13 (the value 13 will come from self::$_matrix_mult_inv)
        self::$_gm13 = array(
            0x00, 0x0d, 0x1a, 0x17, 0x34, 0x39, 0x2e, 0x23, 0x68, 0x65, 0x72, 0x7f, 0x5c, 0x51, 0x46, 0x4b,
            0xd0, 0xdd, 0xca, 0xc7, 0xe4, 0xe9, 0xfe, 0xf3, 0xb8, 0xb5, 0xa2, 0xaf, 0x8c, 0x81, 0x96, 0x9b,
            0xbb, 0xb6, 0xa1, 0xac, 0x8f, 0x82, 0x95, 0x98, 0xd3, 0xde, 0xc9, 0xc4, 0xe7, 0xea, 0xfd, 0xf0,
            0x6b, 0x66, 0x71, 0x7c, 0x5f, 0x52, 0x45, 0x48, 0x03, 0x0e, 0x19, 0x14, 0x37, 0x3a, 0x2d, 0x20,
            0x6d, 0x60, 0x77, 0x7a, 0x59, 0x54, 0x43, 0x4e, 0x05, 0x08, 0x1f, 0x12, 0x31, 0x3c, 0x2b, 0x26,
            0xbd, 0xb0, 0xa7, 0xaa, 0x89, 0x84, 0x93, 0x9e, 0xd5, 0xd8, 0xcf, 0xc2, 0xe1, 0xec, 0xfb, 0xf6,
            0xd6, 0xdb, 0xcc, 0xc1, 0xe2, 0xef, 0xf8, 0xf5, 0xbe, 0xb3, 0xa4, 0xa9, 0x8a, 0x87, 0x90, 0x9d,
            0x06, 0x0b, 0x1c, 0x11, 0x32, 0x3f, 0x28, 0x25, 0x6e, 0x63, 0x74, 0x79, 0x5a, 0x57, 0x40, 0x4d,
            0xda, 0xd7, 0xc0, 0xcd, 0xee, 0xe3, 0xf4, 0xf9, 0xb2, 0xbf, 0xa8, 0xa5, 0x86, 0x8b, 0x9c, 0x91,
            0x0a, 0x07, 0x10, 0x1d, 0x3e, 0x33, 0x24, 0x29, 0x62, 0x6f, 0x78, 0x75, 0x56, 0x5b, 0x4c, 0x41,
            0x61, 0x6c, 0x7b, 0x76, 0x55, 0x58, 0x4f, 0x42, 0x09, 0x04, 0x13, 0x1e, 0x3d, 0x30, 0x27, 0x2a,
            0xb1, 0xbc, 0xab, 0xa6, 0x85, 0x88, 0x9f, 0x92, 0xd9, 0xd4, 0xc3, 0xce, 0xed, 0xe0, 0xf7, 0xfa,
            0xb7, 0xba, 0xad, 0xa0, 0x83, 0x8e, 0x99, 0x94, 0xdf, 0xd2, 0xc5, 0xc8, 0xeb, 0xe6, 0xf1, 0xfc,
            0x67, 0x6a, 0x7d, 0x70, 0x53, 0x5e, 0x49, 0x44, 0x0f, 0x02, 0x15, 0x18, 0x3b, 0x36, 0x21, 0x2c,
            0x0c, 0x01, 0x16, 0x1b, 0x38, 0x35, 0x22, 0x2f, 0x64, 0x69, 0x7e, 0x73, 0x50, 0x5d, 0x4a, 0x47,
            0xdc, 0xd1, 0xc6, 0xcb, 0xe8, 0xe5, 0xf2, 0xff, 0xb4, 0xb9, 0xae, 0xa3, 0x80, 0x8d, 0x9a, 0x97
        );

        // multiply a byte by 14 (the value 14 will come from self::$_matrix_mult_inv)
        self::$_gm14 = array(
            0x00, 0x0e, 0x1c, 0x12, 0x38, 0x36, 0x24, 0x2a, 0x70, 0x7e, 0x6c, 0x62, 0x48, 0x46, 0x54, 0x5a,
            0xe0, 0xee, 0xfc, 0xf2, 0xd8, 0xd6, 0xc4, 0xca, 0x90, 0x9e, 0x8c, 0x82, 0xa8, 0xa6, 0xb4, 0xba,
            0xdb, 0xd5, 0xc7, 0xc9, 0xe3, 0xed, 0xff, 0xf1, 0xab, 0xa5, 0xb7, 0xb9, 0x93, 0x9d, 0x8f, 0x81,
            0x3b, 0x35, 0x27, 0x29, 0x03, 0x0d, 0x1f, 0x11, 0x4b, 0x45, 0x57, 0x59, 0x73, 0x7d, 0x6f, 0x61,
            0xad, 0xa3, 0xb1, 0xbf, 0x95, 0x9b, 0x89, 0x87, 0xdd, 0xd3, 0xc1, 0xcf, 0xe5, 0xeb, 0xf9, 0xf7,
            0x4d, 0x43, 0x51, 0x5f, 0x75, 0x7b, 0x69, 0x67, 0x3d, 0x33, 0x21, 0x2f, 0x05, 0x0b, 0x19, 0x17,
            0x76, 0x78, 0x6a, 0x64, 0x4e, 0x40, 0x52, 0x5c, 0x06, 0x08, 0x1a, 0x14, 0x3e, 0x30, 0x22, 0x2c,
            0x96, 0x98, 0x8a, 0x84, 0xae, 0xa0, 0xb2, 0xbc, 0xe6, 0xe8, 0xfa, 0xf4, 0xde, 0xd0, 0xc2, 0xcc,
            0x41, 0x4f, 0x5d, 0x53, 0x79, 0x77, 0x65, 0x6b, 0x31, 0x3f, 0x2d, 0x23, 0x09, 0x07, 0x15, 0x1b,
            0xa1, 0xaf, 0xbd, 0xb3, 0x99, 0x97, 0x85, 0x8b, 0xd1, 0xdf, 0xcd, 0xc3, 0xe9, 0xe7, 0xf5, 0xfb,
            0x9a, 0x94, 0x86, 0x88, 0xa2, 0xac, 0xbe, 0xb0, 0xea, 0xe4, 0xf6, 0xf8, 0xd2, 0xdc, 0xce, 0xc0,
            0x7a, 0x74, 0x66, 0x68, 0x42, 0x4c, 0x5e, 0x50, 0x0a, 0x04, 0x16, 0x18, 0x32, 0x3c, 0x2e, 0x20,
            0xec, 0xe2, 0xf0, 0xfe, 0xd4, 0xda, 0xc8, 0xc6, 0x9c, 0x92, 0x80, 0x8e, 0xa4, 0xaa, 0xb8, 0xb6,
            0x0c, 0x02, 0x10, 0x1e, 0x34, 0x3a, 0x28, 0x26, 0x7c, 0x72, 0x60, 0x6e, 0x44, 0x4a, 0x58, 0x56,
            0x37, 0x39, 0x2b, 0x25, 0x0f, 0x01, 0x13, 0x1d, 0x47, 0x49, 0x5b, 0x55, 0x7f, 0x71, 0x63, 0x6d,
            0xd7, 0xd9, 0xcb, 0xc5, 0xef, 0xe1, 0xf3, 0xfd, 0xa7, 0xa9, 0xbb, 0xb5, 0x9f, 0x91, 0x83, 0x8d
        );
    }
}
?>
